package cn.bonoon.kernel.descriptors.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.util.ClassUtils;

import javassist.Modifier;
import cn.bonoon.kernel.annotations.Ignore;
import cn.bonoon.kernel.descriptors.ClassDescriptor;
import cn.bonoon.kernel.descriptors.ClassPropertyAccessor;

abstract class AbstractClassDescriptor<T extends ClassPropertyAccessor> implements ClassDescriptor{
	protected final Class<?> clazz;
	/**
	 * 解析结束后，该map就是只读的，不应该再往里面写值
	 */
	protected final Map<String, T> properties;
	protected AbstractClassDescriptor(Class<?> clazz){
		this.clazz = clazz;
		this.properties = new HashMap<String, T>();
	}
	
	/**
	 * <pre>
	 * 初始化类的描述器；
	 * 初始化的时候需要对该类所定义的字段进行解析处理。
	 * 
	 * 子类可以重写该方法来提供初始化的其他行为
	 * </pre>
	 * @return
	 */
	public ClassDescriptor initDescriptor(){
		Class<?> next = clazz;
		while (!filted(next)) {
			for (Field field : next.getDeclaredFields()) {
				if (filted(field)) continue;
				T property = parseProperty(field);
				if(null != property){
					properties.put(property.getName(), property);
				}
			}
			next = next.getSuperclass();
		}
		return this;
	}
	
	/**
	 * 类类型的过滤器
	 * @param cls 类类型
	 * @return true表示需要被过滤
	 */
	protected boolean filted(Class<?> cls){
		return null != cls.getAnnotation(Ignore.class) || Object.class.equals(cls);
	}

	/**
	 * 过滤器
	 * 
	 * @param field
	 * @return 是否过滤，true为过滤(忽略掉该值)
	 */
	protected boolean filted(Field field) {
		return Modifier.isStatic(field.getModifiers()) || null != field.getAnnotation(Ignore.class);
	}
	
	protected abstract T parseProperty(Field field);

	public final <A extends Annotation> A getAnnotation(Class<A> annotationClass){
		Class<?> cls = clazz;
		A a = null;
		for(;;){
			a = cls.getAnnotation(annotationClass);
			if(a != null) break;
			cls = cls.getSuperclass();
			if(Object.class.equals(cls))break;
		}
		return a;
	}
	
	public final Class<?> toClass(){
		return clazz;
	}

	/**
	 * clazz.getName()
	 * @return clazz.getName()
	 */
	@Override
	public final String getName() {
		return clazz.getName();
	}
	@Override
	public final String getSimpleName(){
		return clazz.getSimpleName();
	}
	
	public final T get(String name){
		return properties.get(name);
	}
	
	public final Collection<T> values(){
		return properties.values();
	}
	
	@Override
	public int size() {
		return properties.size();
	}
	
	@Override
	public boolean isAssignable(Class<?> cls) {
		return ClassUtils.isAssignable(cls, clazz);
	}
}
